/*
 * Copyright 2012-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.condition;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.annotation.Conditional;

/**
 * {@link Conditional @Conditional} 条件注解，仅当 {@link BeanFactory} 中已经包含指定类的
 * Bean 且可以确定单个候选 Bean 时才匹配。
 * <p>
 * 如果 {@link BeanFactory} 中存在多个匹配的 Bean 实例，但定义了一个主要（primary）候选 Bean，
 * 条件也会匹配。本质上，该条件在自动装配指定类型的 Bean 能够成功时成立。
 * <p>
 * 该条件只能匹配到当前应用上下文已处理过的 Bean 定义，因此强烈建议仅在自动配置类中使用此条件。
 * 如果候选 Bean 可能由其他自动配置创建，请确保使用此条件的自动配置在其之后运行。
 *
 * @author Stephane Nicoll
 * @since 1.3.0
 */
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnSingleCandidate {

	/**
	 * 要检查的 Bean 类型。
	 * 当 {@link BeanFactory} 中存在该类型的 Bean，且在多个实例时存在主要（primary）候选 Bean 时，
	 * 条件成立。
	 * <p>
	 * 此属性<strong>不能</strong>与 {@link #type()} 同时使用，但可以替代 {@link #type()}。
	 * @return 要检查的 Bean 的类型
	 */
	Class<?> value() default Object.class;

	/**
	 * 要检查的 Bean 类型名称（类的全限定名）。
	 * 当 {@link BeanFactory} 中存在该类型的 Bean，且在多个实例时存在主要（primary）候选 Bean 时，
	 * 条件成立。
	 * <p>
	 * 此属性<strong>不能</strong>与 {@link #value()} 同时使用，但可以替代 {@link #value()}。
	 * @return 要检查的 Bean 类型名称
	 */
	String type() default "";

	/**
	 * 决定是否考虑应用上下文层次结构（父上下文）的策略。
	 * @return 搜索策略
	 */
	SearchStrategy search() default SearchStrategy.ALL;

}
